#include "FPCH.h"

#include "FMetricsFreeType.h"

#include "FKerningTable.h"
#include "FLoaderFreeType.h"

namespace Font
{

	MetricsFreeType::MetricsFreeType(Library* a_Library, Loader* a_Loader, Family* a_Family, FT_Face a_Face) : Metrics(a_Library, a_Loader, a_Family)
	{
		m_Face = a_Face;

		FT_ULong char_lowest = 0xFFFFFFFF;
		FT_ULong char_highest = 0;

		Range* range_curr = NULL;

		FT_UInt index;
		FT_ULong char_curr = FT_Get_First_Char(m_Face, &index);
		do
		{
			if (!range_curr)
			{
				range_curr = new Range;
				range_curr->start = char_curr;
				range_curr->end = char_curr;
				m_Ranges.push_back(range_curr);
			}
			else if (char_curr != range_curr->end + 1)
			{
				range_curr = new Range;
				range_curr->start = char_curr;
				range_curr->end = char_curr;
				m_Ranges.push_back(range_curr);
			}
			else
			{
				range_curr->end++;
			}

			m_CharCodes.push_back(char_curr);

			if (char_curr > char_highest) { char_highest = char_curr; }
			if (char_curr < char_lowest) { char_lowest = char_curr; }

			char_curr = FT_Get_Next_Char(m_Face, char_curr, &index);
		}
		while (index != 0);

		m_RangeStart = (unsigned int)char_lowest;
		m_RangeEnd = (unsigned int)char_highest;

		m_Style = FSTYLE_NORMAL;

		if (m_Face->style_flags & FT_STYLE_FLAG_ITALIC)
		{
			m_Style |= FSTYLE_ITALIC;
		}
		if (m_Face->style_flags & FT_STYLE_FLAG_BOLD)
		{
			m_Style |= FWEIGHT_BOLD;
		}
		else
		{
			m_Style |= FWEIGHT_NORMAL;
		}

		m_Transform[0] = 1.f;
		m_Transform[1] = 0.f;
		m_Transform[2] = 0.f;
		m_Transform[3] = 1.f;
	}
	
	MetricsFreeType::MetricsFreeType(Library* a_Library, Loader* a_Loader, Family* a_Family, unsigned int a_Style, CharMap* a_CharMap) : Metrics(a_Library, a_Loader, a_Family)
	{
		LoaderFreeType* loader = (LoaderFreeType*)a_Loader;

		m_RangeStart = a_CharMap->GetRangeStart();
		m_RangeEnd = a_CharMap->GetRangeEnd();

		m_Style = a_Style;
		
		m_Transform[0] = 1.f;
		if (m_Style & FWEIGHT_BOLD)
		{
			m_Transform[0] = 1.5f;
		}
		else if (m_Style & FWEIGHT_THIN)
		{
			m_Transform[0] = 0.95f;
		}

		m_Transform[1] = 0.f;
		if (m_Style & FSTYLE_ITALIC)
		{
			m_Transform[1] = 0.207f;
		}
	}

	MetricsFreeType::~MetricsFreeType()
	{
		
	}

	FaceHandle* MetricsFreeType::CreateFace(unsigned int a_Size, unsigned int a_Options /*= 0*/, CharMap* a_Map /*= NULL*/)
	{
		m_Quality = (a_Options & FSETTING_QUALITY_LOW) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL;
		bool kerning = (a_Options & FSETTING_KERNING_OFF) ? false : true;

		FaceHandle* result = NULL;

		FT_Error error;

		// set style

		error = FT_Set_Char_Size(m_Face, 0, a_Size << 6, 0, 96);
		if (error != 0)
		{
			return result;
		}

		int factor = 1 << 16;

		FT_Matrix transform;
		transform.xx = (int)(m_Transform[0] * factor);
		transform.xy = (int)(m_Transform[1] * factor);
		transform.yx = 0;
		transform.yy = factor;

		FT_Set_Transform(m_Face, &transform, NULL);

		// create face

		unsigned int line_height = m_Face->size->metrics.height >> 6;

		//result = new FaceHandle(m_Library, m_Family, a_Size, line_height);
		result = this->CreateFont(a_Size, line_height);

		// get char map

		CharMap* font_map = NULL;
		if (!a_Map)
		{
			font_map = m_Loader->GetCharMap(m_Family->GetName());

			if (!font_map)
			{
				font_map = new CharMap(m_Library);
				for (size_t i = 0; i < m_Ranges.size(); i++)
				{
					font_map->AddKnownRange(m_Ranges[i]->start, m_Ranges[i]->end);
				}

				font_map->SetDefault(L'?');

				m_Loader->SetCharMap(m_Family->GetName(), font_map);
			}
		}
		else
		{
			font_map = a_Map;
		}

		result->SetCharMap(font_map);

		// get glyphs

		KerningTable* table = NULL;

		result->SetGlyphRange(m_RangeStart, m_RangeEnd);
		
		for (size_t i = 0; i < m_CharCodes.size(); i++)
		{
			int glyph_left = FT_Get_Char_Index(m_Face, m_CharCodes[i]);
			if (glyph_left == 0) { continue; }

			error = FT_Load_Glyph(m_Face, glyph_left, FT_LOAD_DEFAULT);
			if (error != 0) { continue; }

			Glyph* glyph_write = result->GetNextGlyph((unsigned int)m_CharCodes[i]);

			FT_GlyphSlot glyph_curr = m_Face->glyph;

			tb::Pos2 position;
			position.x = (glyph_curr->metrics.horiBearingX) >> 6;
			position.y = ((glyph_curr->metrics.vertAdvance - glyph_curr->metrics.horiBearingY) >> 6);
			tb::Pos2 advance;
			advance.x = (int)(m_Transform[0] * ((glyph_curr->metrics.horiAdvance) >> 6));
			advance.y = (glyph_curr->metrics.vertAdvance) >> 6;

			glyph_write->SetProperties((wchar_t)m_CharCodes[i], position, advance);

			error = FT_Render_Glyph(glyph_curr, m_Quality);
			if (error != 0 || !glyph_curr->bitmap.buffer) { continue; }

			glyph_write->SetBitmap(glyph_curr->bitmap.width, glyph_curr->bitmap.rows, 1);

			if (m_Quality == FT_RENDER_MODE_NORMAL)
			{
				unsigned char* src_line = glyph_curr->bitmap.buffer;
				unsigned int src_pitch = glyph_curr->bitmap.pitch;

				unsigned char* dst_line = glyph_write->GetBitmapData();
				unsigned int dst_pitch = glyph_curr->bitmap.width;

				for (int y = 0; y < glyph_curr->bitmap.rows; y++)
				{
					unsigned char* src = src_line;
					unsigned char* dst = dst_line;

					for (int x = 0; x < glyph_curr->bitmap.width; x++)
					{
						*dst++ = *src++;
					}

					src_line += src_pitch;
					dst_line += dst_pitch;
				}
			}
			else if (m_Quality == FT_RENDER_MODE_MONO)
			{
				unsigned char* src_line = glyph_curr->bitmap.buffer;
				unsigned int src_pitch = glyph_curr->bitmap.pitch;

				unsigned char* dst_line = glyph_write->GetBitmapData();
				unsigned int dst_pitch = glyph_curr->bitmap.width;

				for (int y = 0; y < glyph_curr->bitmap.rows; y++)
				{
					int mask = 0x80;

					unsigned char* src = src_line;
					unsigned char* dst = dst_line;

					for (int x = 0; x < glyph_curr->bitmap.width; x++)
					{
						*dst++ = ((*src & mask) == mask) ? 0xFF : 0x00;

						mask >>= 1;
						if (mask <= 0)
						{
							mask = 0x80;
							src++;
						}
					}

					src_line += src_pitch;
					dst_line += dst_pitch;
				}
			}

			glyph_write->CalculateBoundingBox();

			// kerning

			if (kerning)
			{
				for (size_t j = i + 1; j < m_CharCodes.size(); j++)
				{
					int glyph_right = FT_Get_Char_Index(m_Face, m_CharCodes[j]);
					if (glyph_right == 0) { continue; }

					FT_Vector vec_kerning;

					error = FT_Get_Kerning(m_Face, glyph_left, glyph_right, FT_KERNING_DEFAULT, &vec_kerning);
					if (error == 0 && vec_kerning.x != 0)
					{
						if (!table) { table = new KerningTable(m_Library); }

						table->AddEntry(m_CharCodes[i], m_CharCodes[j], vec_kerning.x >> 6);
					}

					error = FT_Get_Kerning(m_Face, glyph_right, glyph_left, FT_KERNING_DEFAULT, &vec_kerning);
					if (error == 0 && vec_kerning.x != 0)
					{
						if (!table) { table = new KerningTable(m_Library); }

						table->AddEntry(m_CharCodes[j], m_CharCodes[i], vec_kerning.x >> 6);
					}
				}
			}
		}

		result->SetKerningTable(table);

		return result;
	}

}; // namespace Font